﻿//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
// Exo² samples - networkest
// Copyright (C) 2007-2009 Samuel Lacroix and Raphaël Legouge
//
// Distributed under GNU General Public License
// See the License folder of Exo² for details
//
//
// http://www.codeplex.com/exo2 (eng)
//-----------------------------------------------------------------------------


#include "stdafx.h"
#pragma hdrstop
#include "test.h"

                        
CTest Test;

CTest* CTest::pTest = NULL;


CTest::CTest() :
pDev(NULL),
ControlClient0(true),
RenderClient0(true),
NetGraph(true)
{
        this->DXUTExtensions = false;
        DXUTSetCursorSettings(true, true); 
        CTest::pTest = this;
}                                  


void CTest::RegisterGameActions()                      
{                       

}
                    

void CTest::DeviceCreated()
{
        this->pDev = Exo2::CRenderer::Instance().GetD3DDevice();
     
}

                                              
void CTest::DeviceDestroyed()
{
        this->pDev = NULL;   
}                                        


void CTest::DeviceReset() 
{
        Exo2::CRenderer::Instance().EnableLighting(true);
        Exo2::CRenderer::Instance().EnableSpecularLighting(true);
}
                            

void CTest::DeviceLost()                 
{                       

}                                      


void CTest::RegisterConfigProperties()
{                                      
        CApplication::RegisterConfigProperties();
}

void CTest::OnCreated()         
{        
    // NetGraph
        const shared_ptr<Exo2::CSpriteFont> pSF = Exo2::CResourceManager::Instance().GetResource<Exo2::CSpriteFont>(_T("data/fonts/tahoma32.ssf"));
        this->pNetgraphText.reset(new Exo2::CSpriteText(pSF));
        this->pNetgraphText->SetDisplayRect(Exo2::CDim2DRect(Exo2::CHorizontalDim(0, 0.), Exo2::CVerticalDim(0., 0.5), Exo2::CHorizontalDim(0., 0.5), Exo2::CVerticalDim(0, 1.)));
        this->pNetgraphText->SetTextAlignment(Exo2::ALIGN_BOTTOM | Exo2::ALIGN_LEFT);
        this->pNetgraphText->SetTextHeight(Exo2::CVerticalDim(0, 22 * .001));
        this->NetgraphTitles.push_back(_T("Local player : "));
        this->NetgraphTitles.push_back(_T("Other players : "));
        this->NetgraphTitles.push_back(_T("Other entities : "));
        this->NetgraphTitles.push_back(_T("Sounds : "));
        this->NetgraphTitles.push_back(_T("Temporary entity : "));
        this->NetgraphTitles.push_back(_T("User messages : "));
        this->NetgraphTitles.push_back(_T("Entity messages : "));
        this->NetgraphTitles.push_back(_T("String table : "));
        this->NetgraphTitles.push_back(_T("String commands : "));
        this->NetgraphTitles.push_back(_T("Player voice : "));
        
    // Network
        // Initialisation
        if (::enet_initialize() < 0)
            EXO_THROW(Exo2::Exception(Exo2::Strings(Exo2::STR_UNABLE_TO_INITIALIZE_NETWORK_SUBSYSTEM)));
        // Création & enregistrement
        this->pNetworkService.reset(new Exo2::CNetService);
        this->pClient0.reset(new CNetClient);
        this->pNetworkService->Register(*this->pClient0);
        this->pClient1.reset(new CNetClient);
        this->pNetworkService->Register(*this->pClient1);

    // Commandes
        shared_ptr<Exo2::CShellCommand> pCommand;
        
        #define ADD_COMMAND(Name, Usage, Description, Function)\
        pCommand.reset(new Exo2::CShellCommand((Name), (Usage), (Description), boost::bind(&Function, boost::ref(*this), _1), Exo2::CShell::EndUserScope));\
        Shell().RegisterCommand(pCommand)
        
        // Serveur
        ADD_COMMAND(_T("sv_create"),                    _T("sv_create port [password=\"\"]"),                           _T("Create a server on the specified port"),                    CTest::CreateServerCmd);
        ADD_COMMAND(_T("sv_destroy"),                   _T("sv_destroy"),                                               _T("Destroy the created server"),                               CTest::DestroyServerCmd);
        ADD_COMMAND(_T("sv_kick"),                      _T("sv_kick {playername|playerid}"),                            _T("Disconnect the selected peer"),                             CTest::ServerKickCmd);
        ADD_COMMAND(_T("sv_ban"),                       _T("sv_ban  {ipaddress|playername|playerid} [kick=1]"),         _T("Ban the selected player or ban IP"),                        CTest::ServerBanCmd);
        ADD_COMMAND(_T("sv_unban"),                     _T("sv_unban ipaddress"),                                       _T("Unban the selected ip address"),                            CTest::ServerUnbanCmd);
        ADD_COMMAND(_T("sv_banned"),                    _T("sv_banned"),                                                _T("Return the ip address black-list"),                         CTest::ServerBannedCmd);
        ADD_COMMAND(_T("sv_peers_info"),                _T("sv_peers_info"),                                            _T("Return informations about peers"),                          CTest::ServerPeersInfoCmd);
        ADD_COMMAND(_T("sv_welcome_message"),           _T("sv_welcome_message [message]"),                             _T("Get or set the welcome message"),                           CTest::ServerWelcomMessageCmd);
        ADD_COMMAND(_T("sv_players_info"),              _T("sv_players_info"),                                          _T("Return informations about players"),                        CTest::ServerPlayersInfoCmd);
        ADD_COMMAND(_T("sv_say"),                       _T("sv_say message"),                                           _T("Send a message to all players"),                            CTest::ServerSayCmd);
        ADD_COMMAND(_T("sv_unlag"),                     _T("sv_unlag [enable]"),                                        _T("Get or set lag compensation system activation state"),      CTest::ServerUnlagCmd);
        ADD_COMMAND(_T("sv_max_unlag"),                 _T("sv_max_unlag [duration]"),                                  _T("Get or set the maximum time of compensation"),              CTest::ServerMaxUnlagCmd);

        // Application
        ADD_COMMAND(_T("app_switch_control"),           _T("app_switch_control"),                                       _T("Switch the control of the current player"),                 CTest::SwitchControlCmd);
        ADD_COMMAND(_T("app_switch_render"),            _T("app_switch_render"),                                        _T("Switch the render of the current player"),                  CTest::SwitchRenderCmd);
        ADD_COMMAND(_T("app_netgraph"),                 _T("app_netgraph"),                                             _T("Return statistics about the network"),                      CTest::NetgraphCmd);
        
        // Clients
        ADD_COMMAND(_T("cl0_join"),                     _T("cl0_join hostname port playername [password=\"\"]"),        _T("Connect to the specified server"),                          CTest::Join0Cmd);
        ADD_COMMAND(_T("cl1_join"),                     _T("cl1_join hostname port playername [password=\"\"]"),        _T("Connect to the specified server"),                          CTest::Join1Cmd);
        ADD_COMMAND(_T("cl0_quit"),                     _T("cl0_quit"),                                                 _T("Disconnect from the server"),                               CTest::Quit0Cmd);
        ADD_COMMAND(_T("cl1_quit"),                     _T("cl1_quit"),                                                 _T("Disconnect from the server"),                               CTest::Quit1Cmd);
        ADD_COMMAND(_T("cl0_name"),                     _T("cl0_name [name]"),                                          _T("Get or set the current name of the client"),                CTest::Name0Cmd);
        ADD_COMMAND(_T("cl1_name"),                     _T("cl1_name [name]"),                                          _T("Get or set the current name of the client"),                CTest::Name1Cmd);
        ADD_COMMAND(_T("cl0_up_rate"),                  _T("cl0_uprate [rate]"),                                        _T("Get or set the current client update rate"),                CTest::UpRate0Cmd);
        ADD_COMMAND(_T("cl1_up_rate"),                  _T("cl1_uprate [rate]"),                                        _T("Get or set the current client update rate"),                CTest::UpRate1Cmd);
        ADD_COMMAND(_T("cl0_cmd_rate"),                 _T("cl0_cmd_rate [rate]"),                                      _T("Get or set the current client command rate"),               CTest::CmdRate0Cmd);
        ADD_COMMAND(_T("cl1_cmd_rate"),                 _T("cl1_cmd_rate [rate]"),                                      _T("Get or set the current client command rate"),               CTest::CmdRate1Cmd);
        ADD_COMMAND(_T("cl0_interp"),                   _T("cl0_interp [duration]"),                                    _T("Get or set the interpolation duration"),                    CTest::Interp0Cmd);
        ADD_COMMAND(_T("cl1_interp"),                   _T("cl1_interp [duration]"),                                    _T("Get or set the interpolation duration"),                    CTest::Interp1Cmd);
        ADD_COMMAND(_T("cl0_predict"),                  _T("cl0_predict [enable]"),                                     _T("Enable or disable the prediction"),                         CTest::Predict0Cmd);
        ADD_COMMAND(_T("cl1_predict"),                  _T("cl1_predict [enable]"),                                     _T("Enable or disable the prediction"),                         CTest::Predict1Cmd);
        ADD_COMMAND(_T("cl0_extrapol"),                 _T("cl0_extrapol [enable]"),                                    _T("Enable or disable the extrapolation"),                      CTest::Extrapol0Cmd);
        ADD_COMMAND(_T("cl1_extrapol"),                 _T("cl1_extrapol [enable]"),                                    _T("Enable or disable the extrapolation"),                      CTest::Extrapol1Cmd);
        ADD_COMMAND(_T("cl0_say"),                      _T("cl0_say Message"),                                          _T("Send Message to all players"),                              CTest::Say0Cmd);
        ADD_COMMAND(_T("cl1_say"),                      _T("cl1_say Message"),                                          _T("Send Message to all players"),                              CTest::Say1Cmd);
        
                
        #undef ADD_COMMAND
}                                                                
                                             

void CTest::OnDestroying()                       
{
        this->pServer.reset();
        this->pClient0.reset();
        this->pClient1.reset();
        this->pNetworkService.reset();
        ::enet_deinitialize();
        
        this->pNetgraphText.reset();
}                        
                                                    

void CTest::Update(double TotalTime, float ElapsedTime)
{
    // Network
        this->pNetworkService->Update();
        
        if (this->pClient0)
        {
                this->pClient0->EnableInput(this->ControlClient0);
                this->pClient0->Update(TotalTime, ElapsedTime);
                if (this->pClient0->GetCurrentState().ConnectionState == Exo2::CS_CONNECTED && this->ControlClient0 && this->NetGraph)
                {
                        this->pNetgraphText->SetText(GetNetgraphString(this->pClient0));
                }
        }
            
        if (this->pClient1)
        {
                this->pClient1->EnableInput(!this->ControlClient0);
                this->pClient1->Update(TotalTime, ElapsedTime);
                if (!this->ControlClient0 && this->NetGraph)
                {
                        this->pNetgraphText->SetText(GetNetgraphString(this->pClient1));
                }
        }
        
        if (this->pServer)
        {
                this->pServer->Update(TotalTime, ElapsedTime);
        }
}                       
                      

void CTest::Render3D() const
{
        EXO_DXTIF(this->pDev->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xFF000000, 1.f, 0));        
        
        Exo2::CRenderer::Instance().EnableLighting(true);
        Exo2::CRenderer::Instance().EnableSpecularLighting(true);
        

}                       
                                                       
                        
void CTest::Render2D() const
{ 
        if (this->NetGraph)
            this->pNetgraphText->Render2D();
            
        if (this->pClient0 && this->RenderClient0)
            this->pClient0->Render2D();

        if (this->pClient1 && !this->RenderClient0)
            this->pClient1->Render2D();
} 

CTest* CTest::GetTest()
{
        return CTest::pTest;
}


tstring CTest::GetNetgraphString(const scoped_ptr<CNetClient>& pClient)
{
        tostringstream ResultString;
        ResultString << _T("Netgraph :") << std::endl;
        if (!pClient->GetPlayers().empty())
            ResultString << _T("  Ping : ") << pClient->GetPlayers()[pClient->GetMyID()]->Ping << std::endl;
        ResultString << _T("In : ") << std::endl;
        ResultString << _T("  Update rate : ") << pClient->GetUpdateRate() << std::endl;
        double Total = 0.;
        const std::vector<double>& Result = pClient->GetLastNetgraphResult();
        for (size_t i = 0; i < Result.size(); ++i)
        {
                const double Value = Result[i];
                Total += Value;
                if (!Exo2::Utils::IsZero(Value) && Value > 0.)
                {
                        if (Value > 1024.)
                        {
                                ResultString << _T("  ") << this->NetgraphTitles[i] << size_t(Value / 1024.) << _T("ko/s");
                        }
                        else
                        {
                                ResultString << _T("  ") << this->NetgraphTitles[i] << size_t(Value) << _T("o/s");
                        }
                        ResultString << std::endl;
                }
        }
        if (Total > 0.)
            ResultString << _T("  Total : ") << size_t(Total / 1024.) << _T("ko/s") << std::endl;
        ResultString << _T("  Lost : ") << pClient->GetLostPacketRate() << _T(" packet(s)/s (") << pClient->GetPacketLostPercent() << _T("%)") << std::endl;
        ResultString << _T("Out : ") << std::endl;
        ResultString << _T("  Command rate : ") << pClient->GetCommandRate() << std::endl;
        ResultString << _T("Options :") << std::endl;
        if (pClient->GetInterpolation() > 0.)
            ResultString << _T("  Interpolation : ") << pClient->GetInterpolation() << std::endl;
        else
            ResultString << _T("  Interpolation : off") << std::endl;
        ResultString << _T("  ") << (pClient->IsExtrapolationAllowed() ? _T("Extrapolation : on") : _T("Extrapolation : off")) << std::endl;
        ResultString << _T("  ") << (pClient->IsPredicting() ? _T("Prediction : on") : _T("Prediction : off"));
        return ResultString.str();
}


bool CTest::CreateServerCmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Exo2::Utils::ToLower(Instructions[0]) == _T("sv_create"));

        tstring Password = _T("");
        unsigned short Port = 0;
        try
        {
                if (this->pServer)      // *** Peut-être notifier
                    return true;
                    
                switch (Instructions.size())
                {                             
                    case 3:     // CreateServer Port Password
                        Password = Instructions[2];
                    case 2:     // CreateServer Port
                        Port = Exo2::Utils::StringToValue<WORD>(Instructions[1]);
                        this->pServer.reset(new CNetServer(Port, Password));
                        this->pNetworkService->Register(*this->pServer);

                        return true;

                default:
                        return false;
                }
        }
        catch (Exo2::Exception& e)
        {
                Exo2::CLogger::Instance().Log(e.Message);
                return true;
        }
}


bool CTest::DestroyServerCmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Exo2::Utils::ToLower(Instructions[0]) == _T("sv_destroy"));

        try
        {
                switch (Instructions.size())
                {
                case 1:     // DestroyServer
                        this->pServer.reset();

                        return true;

                default:
                        return false;
                }
        }
        catch (Exo2::Exception& e)
        {
                Exo2::CLogger::Instance().Log(e.Message);
                return true;
        }
}


bool CTest::SwitchControlCmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Exo2::Utils::ToLower(Instructions[0]) == _T("app_switch_control"));
        
        try
        {
                if (Instructions.size() == 1)
                {
                        if ((this->ControlClient0 = !this->ControlClient0) != false)            // voir ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/dv_vccomp/html/89cd3f4f-812c-4a4b-9426-65a5a6d1b99c.htm
                        {
                                this->pClient0->EnableInput(true);
                                this->pClient1->EnableInput(false);
                                Exo2::CLogger::Instance().Log(_T("Now controling player 0"));          // *** String
                        }
                        else
                        {
                                this->pClient0->EnableInput(false);
                                this->pClient1->EnableInput(true);
                                Exo2::CLogger::Instance().Log(_T("Now controling player 1"));          // *** String
                        }
                        return true;
                }
                else
                    return false;
        }
        catch (Exo2::Exception& e)
        {
                Exo2::CLogger::Instance().Log(e.Message);
                return true;
        }
}


bool CTest::SwitchRenderCmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Exo2::Utils::ToLower(Instructions[0]) == _T("app_switch_render"));
        
        try
        {
                if (Instructions.size() == 1)
                {
                        if ((this->RenderClient0 = !this->RenderClient0) != false)              // voir ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/dv_vccomp/html/89cd3f4f-812c-4a4b-9426-65a5a6d1b99c.htm
                        {
                                Exo2::CLogger::Instance().Log(_T("Now rendering player 0"));          // *** String
                        }
                        else
                        {
                                Exo2::CLogger::Instance().Log(_T("Now rendering player 1"));          // *** String
                        }
                        return true;
                }
                else
                    return false;
        }
        catch (Exo2::Exception& e)
        {
                Exo2::CLogger::Instance().Log(e.Message);
                return true;
        }
}


bool CTest::ServerKickCmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("sv_kick"));

        try
        {
                if (!this->pServer)
                {
                        Exo2::CLogger::Instance().Log(_T("*** Le serveur n'est pas créé"));
                        return true;
                }
                if (Instructions.size() == 2)
                {
                        // Il faut déterminer si on nous envoie un PlayerID ou un nom de joueur
                        if (Instructions[1].find_first_not_of(_T("1234567890")) == tstring::npos)
                        {       // Il n'y a que des chiffres => PlayerID (le serveur n'autorise pas un nom de player comportant que des chiffres)
                                this->pServer->KickPlayer(Exo2::Utils::StringToValue<size_t>(Instructions[1]));
                        }
                        else
                        {
                                this->pServer->KickPlayer(Instructions[1]);
                        }
                        return true;                        
                }
                else
                        return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::ServerBanCmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("sv_ban"));

        try
        {
                if (!this->pServer)             // *** Peut-être notifier
                {
                        return true;
                }
                bool KickAfterban = true;
                switch (Instructions.size())
                {
                case 3:     //sv_ban PlayerID/PlayerName KickAfterBan
                        KickAfterban = Exo2::Utils::StringToValue<bool>(Instructions[2]);
                case 2:
                        // On regarde si c'est le PlayerID ou bien si c'est le nom
                        if (Instructions[1].find_first_not_of(_T("1234567890")) == tstring::npos)
                        {       // Il n'y a que des chiffres => PlayerID (le serveur n'autorise pas un nom de player comportant que des chiffres)
                                this->pServer->BanPlayer(Exo2::Utils::StringToValue<size_t>(Instructions[1]), KickAfterban);
                        }
                        else if (Exo2::CNetUtils::IsIpAddress(Instructions[1]))  // C'est une adresse IP on ban l'adresse, mais on ne fait rien d'autre
                        {
                                this->pServer->Ban(Instructions[1], false);
                        }
                        else
                        {
                                this->pServer->BanPlayer(Instructions[1], KickAfterban);
                        }
                        return true;
                default:
                        return false;                        
                }
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::ServerUnbanCmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("sv_unban"));

        try
        {
                if (!this->pServer)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 2)
                {
                        this->pServer->Unban(Instructions[1]);
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::ServerBannedCmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("sv_banned"));

        try
        {
                if (!this->pServer)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        const Exo2::INetServer::TIpAddresses& IPs = this->pServer->GetBannedIpAddresses();
                        if (IPs.empty())
                                Exo2::CLogger::Instance().Log(_T("No IP address banned"));
                        else
                        {
                                for (Exo2::INetServer::TIpAddresses::const_iterator pIP = IPs.begin(); pIP != IPs.end(); ++pIP)
                                {
                                        Exo2::CLogger::Instance().Log(*pIP);
                                }

                        }
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
} 


bool CTest::ServerPeersInfoCmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("sv_peers_info"));

        try
        {
                if (!this->pServer)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        size_t PeerNumber = 0;
                        const Exo2::CSlot<Exo2::CNetPeer>::TSlots& Peers = this->pServer->GetPeers();
                        tostringstream oss;
                        for (size_t i = 0; i < Peers.size(); ++i)
                        {
                                if (Peers[i])
                                {
                                        ++PeerNumber;
                                        Exo2::CNetPeer& Peer = *Peers[i];
                                        oss.str(_T(""));
                                        oss <<  i << _T(" : ") << Peer.GetIpAddress();
                                        switch (Peer.ConnectionState)
                                        {
                                        case Exo2::CS_NOT_CONNECTED:
                                                oss << _T(" ") << Exo2::Strings(Exo2::STR_DISCONNECTED);
                                                break;
                                        case Exo2::CS_CONNECTING:
                                                oss << _T(" ") << _T("connecting");     // *** Strings
                                                break;
                                        case Exo2::CS_CONNECTED:
                                                oss << _T(" ") << _T("connected");      // *** Strings
                                                break;
                                        case Exo2::CS_DISCONNECTING:
                                                oss << _T(" ") << _T("disconnecting");  // *** Strings
                                        }
                                        Exo2::CLogger::Instance().Log(oss.str());
                                }
                        }
                        if (PeerNumber == 0)
                                Exo2::CLogger::Instance().Log(_T("No peer connected"));
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
} 


bool CTest::ServerWelcomMessageCmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("sv_welcome_message"));

        try
        {
                if (!this->pServer)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        Exo2::CLogger::Instance().Log(this->pServer->GetWelcomeMessage());
                        return true;
                }
                else if (Instructions.size() == 2)
                {
                        tstring Message = Instructions[1];
                        this->pServer->SetWelcomeMessage(Message);
                        std::vector<tstring> Strings;
                        Strings.push_back(_T("sv_welcomemessage"));
                        Strings.push_back(Message);
                        Exo2::CLogger::Instance().Log(Exo2::Strings(Exo2::STR_ARG1_SET_TO_ARG2, Strings.begin(), Strings.end()));
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::ServerPlayersInfoCmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("sv_players_info"));

        try
        {
                if (!this->pServer)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        size_t PlayerNumber = 0;
                        for (size_t i = 0; i < this->pServer->GetPlayers().size(); ++i)
                        {
                                tostringstream oss;
                                const shared_ptr<CServerPlayer>& pPlayer = this->pServer->GetPlayers()[i];
                                EXO_ASSERT(pPlayer != NULL);
                                
                                switch (pPlayer->State)
                                {
                                    case CServerPlayer::PS_NOT_JOINNED:
                                        {
                                                break;
                                        }
                                    case CServerPlayer::PS_JOINNING:
                                        {
                                                ++PlayerNumber;
                                                oss << pPlayer->Name << _T(" : updaterate=") << pPlayer->UpdateRate << _T("fps interp=");
                                                oss << ((pPlayer->Interpolation > 0.) ? Exo2::Utils::ValueToString(size_t(pPlayer->Interpolation * 1000)) + tstring(_T("ms")) : _T("off"));
                                                oss << _T(" state=");
                                                oss << Exo2::Strings(Exo2::STR_JOINNING);
                                                oss << _T(" playerID=") << i << _T(" peerID=") << pPlayer->PeerID;
                                                Exo2::CLogger::Instance().Log(oss.str());
                                                break;
                                        }
                                    case CServerPlayer::PS_SYNCHRONIZING:
                                        {
                                                ++PlayerNumber;
                                                oss << pPlayer->Name << _T(" : updaterate=") << pPlayer->UpdateRate << _T("fps interp=");
                                                oss << ((pPlayer->Interpolation > 0.) ? Exo2::Utils::ValueToString(size_t(pPlayer->Interpolation * 1000)) + tstring(_T("ms")) : _T("off"));
                                                oss << _T(" state=");
                                                oss << Exo2::Strings(Exo2::STR_SYNCHRONIZING);
                                                oss << _T(" playerID=") << i << _T(" peerID=") << pPlayer->PeerID;
                                                Exo2::CLogger::Instance().Log(oss.str());
                                                break;
                                        }
                                    case CServerPlayer::PS_JOINNED:
                                        {
                                                ++PlayerNumber;
                                                oss << pPlayer->Name << _T(" :") << _T(" ping=") << size_t(pPlayer->Ping * 1000) << _T("ms updaterate=") << pPlayer->UpdateRate << _T("fps interp=");
                                                oss << ((pPlayer->Interpolation > 0.) ? Exo2::Utils::ValueToString(size_t(pPlayer->Interpolation * 1000)) + tstring(_T("ms")) : _T("off"));
                                                oss << _T(" state=");
                                                oss << Exo2::Strings(Exo2::STR_JOINNED) << _T(" (next synchro ") << int(1. / this->pServer->GetSynchronizationRate() - (this->pServer->GetTimeStamp() - pPlayer->LastSynchronizationEnd)) << _T("s)");
                                                oss << _T(" playerID=") << i << _T(" peerID=") << pPlayer->PeerID;
                                                Exo2::CLogger::Instance().Log(oss.str());
                                                break;
                                        }
                                    case CServerPlayer::PS_RESYNCHRONIZING:
                                        {
                                                ++PlayerNumber;
                                                oss << pPlayer->Name << _T(" : updaterate=") << pPlayer->UpdateRate << _T("fps interp=");
                                                oss << ((pPlayer->Interpolation > 0.) ? Exo2::Utils::ValueToString(size_t(pPlayer->Interpolation * 1000)) + tstring(_T("ms")) : _T("off"));
                                                oss << _T(" state=");
                                                oss << Exo2::Strings(Exo2::STR_RESYNCHRONIZING);
                                                oss << _T(" playerID=") << i << _T(" peerID=") << pPlayer->PeerID;
                                                Exo2::CLogger::Instance().Log(oss.str());
                                                break;
                                        }
                                    default:
                                        {
                                                ++PlayerNumber;
                                                oss << pPlayer->Name << _T(" : state=");
                                                oss << Exo2::Strings(Exo2::STR_UNKNONW_STATE);
                                                oss << _T(" playerID=") << i << _T(" peerID=") << pPlayer->PeerID;
                                                Exo2::CLogger::Instance().Log(oss.str());
                                        }
                                } 
                        }
                        if (PlayerNumber == 0)
                                Exo2::CLogger::Instance().Log(_T("No player in the game"));     // *** Strings
                        return true;
                }
                else
                {
                        return false;
                }
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::ServerSayCmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("sv_say"));

        try
        {
                if (!this->pServer)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() >= 2)
                {
                        tstring Message = _T("");
                        for (size_t i = 1; i < Instructions.size(); ++i)
                        {
                                Message += Instructions[i];
                                if (i != Instructions.size())
                                        Message += _T(" ");
                        }
                        this->pServer->Say(Message);
                        tostringstream oss;
                        oss << _T("admin says \"") << Message << _T("\"");      // *** Strings
                        Exo2::CLogger::Instance().Log(oss.str());
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::ServerUnlagCmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("sv_unlag"));

        try
        {
                if (!this->pServer)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 2)
                {
                        bool Enable = Exo2::Utils::StringToValue<bool>(Instructions[1]);
                        this->pServer->GetGameStateManager().Enable(Enable);
                        tstring Args[] = {_T("sv_unlag"), Enable ? _T("1") : _T("0")};
                        Exo2::CLogger::Instance().Log(Exo2::Strings(Exo2::STR_ARG1_SET_TO_ARG2, Args, Args + 2));
                        return true;
                }
                else if (Instructions.size() == 1)
                {
                        Exo2::CLogger::Instance().Log(Exo2::Utils::ValueToString(this->pServer->GetGameStateManager().IsEnabled()));
                        return true;
                }
                
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::ServerMaxUnlagCmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("sv_max_unlag"));

        try
        {
                if (!this->pServer)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 2)
                {
                        double Value = Exo2::Utils::StringToValue<double>(Instructions[1]);
                        this->pServer->GetGameStateManager().SetMaxCompensation(Value);
                        tstring Args[] = {_T("sv_unlag"), Instructions[1]};
                        Exo2::CLogger::Instance().Log(Exo2::Strings(Exo2::STR_ARG1_SET_TO_ARG2, Args, Args + 2));
                        return true;
                }
                else if (Instructions.size() == 1)
                {
                        Exo2::CLogger::Instance().Log(Exo2::Utils::ValueToString(this->pServer->GetGameStateManager().GetMaxCompensation()));
                        return true;
                }

                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::Join0Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl0_join"));

        try
        {
                if (!this->pClient0)             // *** Peut-être notifier
                {
                        return true;
                }
                tstring Password = _T("");
                tstring Name = _T("");
                unsigned short Port = 0;
                tstring Host =_T("");
                switch (Instructions.size())
                {
                    case 5:
                        Password = Instructions[4];
                    case 4:
                        Name = Instructions[3];
                        Port = Exo2::Utils::StringToValue<unsigned short>(Instructions[2]);
                        Host = Instructions[1];
                        this->pClient0->ConnectThenJoin(Host, Port, Name, Password);
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::Quit0Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl0_quit"));

        try
        {
                if (!this->pClient0)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        this->pClient0->QuitThenDisconnect();
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::Join1Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl1_join"));

        try
        {
                if (!this->pClient1)             // *** Peut-être notifier
                {
                        return true;
                }
                tstring Password = _T("");
                tstring Name = _T("");
                unsigned short Port = 0;
                tstring Host =_T("");
                switch (Instructions.size())
                {
                case 5:
                        Password = Instructions[4];
                case 4:
                        Name = Instructions[3];
                        Port = Exo2::Utils::StringToValue<unsigned short>(Instructions[2]);
                        Host = Instructions[1];
                        this->pClient1->ConnectThenJoin(Host, Port, Name, Password);
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::Quit1Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl1_quit"));

        try
        {
                if (!this->pClient1)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        this->pClient1->QuitThenDisconnect();
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::Name0Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl0_name"));

        try
        {
                if (!this->pClient0)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        Exo2::CLogger::Instance().Log(this->pClient0->GetName());
                        return true;
                }
                else if (Instructions.size() == 2)
                {
                        this->pClient0->SendChangeName(Instructions[1]);
                        return true;
                }
                
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::Name1Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl1_name"));

        try
        {
                if (!this->pClient1)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        Exo2::CLogger::Instance().Log(this->pClient1->GetName());
                        return true;
                }
                else if (Instructions.size() == 2)
                {
                        this->pClient1->SendChangeName(Instructions[1]);
                        return true;
                }

                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::UpRate0Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl0_up_rate"));

        try
        {
                if (!this->pClient0)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        Exo2::CLogger::Instance().Log(Exo2::Utils::ValueToString(this->pClient0->GetUpdateRate()));
                        return true;
                }
                else if (Instructions.size() == 2)
                {
                        this->pClient0->SendChangeUpdateRate(Exo2::Utils::StringToValue<double>(Instructions[1]));
                        return true;
                }

                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::UpRate1Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl1_up_rate"));

        try
        {
                if (!this->pClient1)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        Exo2::CLogger::Instance().Log(Exo2::Utils::ValueToString(this->pClient1->GetUpdateRate()));
                        return true;
                }
                else if (Instructions.size() == 2)
                {
                        this->pClient1->SendChangeUpdateRate(Exo2::Utils::StringToValue<double>(Instructions[1]));
                        return true;
                }

                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::CmdRate0Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl0_cmd_rate"));

        try
        {
                if (!this->pClient0)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        Exo2::CLogger::Instance().Log(Exo2::Utils::ValueToString(this->pClient0->GetCommandRate()));
                        return true;
                }
                else if (Instructions.size() == 2)
                {
                        this->pClient0->SetCommandRate(Exo2::Utils::StringToValue<double>(Instructions[1]));
                        tstring Args[] = {_T("cl0_cmd_rate"), Exo2::Utils::ValueToString(this->pClient0->GetCommandRate())};
                        Exo2::CLogger::Instance().Log(Exo2::Strings(Exo2::STR_ARG1_SET_TO_ARG2, Args, Args + 2));
                        return true;
                }

                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
} 


bool CTest::CmdRate1Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl1_cmd_rate"));

        try
        {
                if (!this->pClient1)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        Exo2::CLogger::Instance().Log(Exo2::Utils::ValueToString(this->pClient1->GetCommandRate()));
                        return true;
                }
                else if (Instructions.size() == 2)
                {
                        this->pClient1->SetCommandRate(Exo2::Utils::StringToValue<double>(Instructions[1]));
                        tstring Args[] = {_T("cl1_cmd_rate"), Exo2::Utils::ValueToString(this->pClient1->GetCommandRate())};
                        Exo2::CLogger::Instance().Log(Exo2::Strings(Exo2::STR_ARG1_SET_TO_ARG2, Args, Args + 2));
                        return true;
                }

                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::Interp0Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl0_interp"));

        try
        {
                if (!this->pClient0)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        Exo2::CLogger::Instance().Log(Exo2::Utils::ValueToString(this->pClient0->GetInterpolation()));
                        return true;
                }
                else if (Instructions.size() == 2)
                {
                        this->pClient0->SendChangeInterpolation(Exo2::Utils::StringToValue<double>(Instructions[1]));
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::Interp1Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl1_interp"));

        try
        {
                if (!this->pClient1)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        Exo2::CLogger::Instance().Log(Exo2::Utils::ValueToString(this->pClient1->GetInterpolation()));
                        return true;
                }
                else if (Instructions.size() == 2)
                {
                        this->pClient1->SendChangeInterpolation(Exo2::Utils::StringToValue<double>(Instructions[1]));
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::NetgraphCmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("app_netgraph"));

        try
        {
                if (this->ControlClient0 && !this->pClient0 || !this->ControlClient0 && !this->pClient1)        // *** peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        Exo2::CLogger::Instance().Log(Exo2::Utils::ValueToString(this->NetGraph));
                        return true;
                }
                if (Instructions.size() == 2)
                {
                        this->NetGraph = !this->NetGraph;
                        tstring Args[] = {_T("app_netgraph"), Exo2::Utils::ValueToString(this->NetGraph)};
                        Exo2::CLogger::Instance().Log(Exo2::Strings(Exo2::STR_ARG1_SET_TO_ARG2, Args, Args + 2));
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
} 


bool CTest::Predict0Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl0_predict"));

        try
        {
                if (!this->pClient0)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        Exo2::CLogger::Instance().Log(Exo2::Utils::ValueToString(this->pClient0->IsPredicting()));
                        return true;
                }
                else if (Instructions.size() == 2)
                {
                        this->pClient0->EnablePrediction(Exo2::Utils::StringToValue<bool>(Instructions[1]));
                        tstring Args[] = {_T("cl0_predict"), Exo2::Utils::ValueToString(this->pClient0->IsPredicting())};
                        Exo2::CLogger::Instance().Log(Exo2::Strings(Exo2::STR_ARG1_SET_TO_ARG2, Args, Args + 2));
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::Predict1Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl1_predict"));

        try
        {
                if (!this->pClient1)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        Exo2::CLogger::Instance().Log(Exo2::Utils::ValueToString(this->pClient1->IsPredicting()));
                        return true;
                }
                else if (Instructions.size() == 2)
                {
                        this->pClient1->EnablePrediction(Exo2::Utils::StringToValue<bool>(Instructions[1]));
                        tstring Args[] = {_T("cl1_predict"), Exo2::Utils::ValueToString(this->pClient1->IsPredicting())};
                        Exo2::CLogger::Instance().Log(Exo2::Strings(Exo2::STR_ARG1_SET_TO_ARG2, Args, Args + 2));
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::Extrapol0Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl0_extrapol"));

        try
        {
                if (!this->pClient0)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        Exo2::CLogger::Instance().Log(Exo2::Utils::ValueToString(this->pClient0->IsExtrapolationAllowed()));
                        return true;
                }
                else if (Instructions.size() == 2)
                {       // *** /!\ l'extrapolation ne concerne que le joueur dans ce cas !!
                        this->pClient0->EnableExtrapolation(Exo2::Utils::StringToValue<bool>(Instructions[1]));
                        tstring Args[] = {_T("cl0_extrapol"), Exo2::Utils::ValueToString(this->pClient0->IsExtrapolationAllowed())};
                        Exo2::CLogger::Instance().Log(Exo2::Strings(Exo2::STR_ARG1_SET_TO_ARG2, Args, Args + 2));
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::Extrapol1Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl1_extrapol"));

        try
        {
                if (!this->pClient1)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() == 1)
                {
                        Exo2::CLogger::Instance().Log(Exo2::Utils::ValueToString(this->pClient1->IsExtrapolationAllowed()));
                        return true;
                }
                else if (Instructions.size() == 2)
                {       // *** /!\ l'extrapolation ne concerne que le joueur dans ce cas !!
                        this->pClient1->EnableExtrapolation(Exo2::Utils::StringToValue<bool>(Instructions[1]));
                        tstring Args[] = {_T("cl1_extrapol"), Exo2::Utils::ValueToString(this->pClient1->IsExtrapolationAllowed())};
                        Exo2::CLogger::Instance().Log(Exo2::Strings(Exo2::STR_ARG1_SET_TO_ARG2, Args, Args + 2));
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}


bool CTest::Say0Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl0_say"));

        try
        {
                if (!this->pClient0)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() > 1)
                {
                        tostringstream oss;
                        const size_t Size = Instructions.size();
                        for (size_t i = 1; i < Size; ++i)
                        {
                                oss << Instructions[i];
                                if (i + 1 < Size)
                                    oss << _T(" ");
                        }
                        this->pClient0->Say(oss.str());
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
} 


bool CTest::Say1Cmd(const std::vector<tstring>& Instructions)
{
        EXO_ASSERT(!Instructions.empty());
        EXO_ASSERT(Instructions[0] == _T("cl1_say"));

        try
        {
                if (!this->pClient1)             // *** Peut-être notifier
                {
                        return true;
                }
                if (Instructions.size() > 1)
                {
                        tostringstream oss;
                        const size_t Size = Instructions.size();
                        for (size_t i = 1; i < Size; ++i)
                        {
                                oss << Instructions[i];
                                if (i + 1 < Size)
                                    oss << _T(" ");
                        }
                        this->pClient1->Say(oss.str());
                        return true;
                }
                return false;
        }
        catch (Exo2::Exception& E)
        {
                Exo2::CLogger::Instance().Log(E.Message);
                return true;
        }
}